package com.dp1.deliversoft.algorithm;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;

import com.dp1.deliversoft.entidad.CeldaAux;
import com.dp1.deliversoft.entidad.Constantes;
import com.dp1.deliversoft.entidad.Mapa;
import com.dp1.deliversoft.entidad.Pedido;
import com.dp1.deliversoft.entidad.Ruta;
import com.dp1.deliversoft.entidad.TablaRuteoAux;
import com.dp1.deliversoft.entidad.Tarifas;
import com.dp1.deliversoft.entidad.Vehiculo;
import com.dp1.deliversoft.entidad.resultados.ResultadosAux;

public class rutear {
	
	Tarifas tarifas;
	int cantVehiculosPorAsignar;
	
	Mapa mapa;
	Calculos calc = new Calculos();
	int x_st, y_st, cantCasasPorRecorrer;
	double criterioAleatorioOgoloso;
	int indice_pedidos_lugar_actual;
	TablaRuteoAux tabAux;
	ArrayList<Pedido> pedidos;
	ArrayList<Pedido> casasPorVisitar;
	
	double costoParcial = 0;
	double sumaRestantes;
	int H = 2;
	ArrayList<ResultadosAux> listaResAux = new ArrayList<ResultadosAux>();
	Ruta rutaSolAux = new Ruta();
	int cx, cy;

	

	

	// public boolean CumplenPlazos(Ruta rutaOptimizada) {

	// }

	/*
	 * public void prueba() { Hashtable<Integer, CeldaAux> tbSubRuteo
	 * =tabAux.getTbRuteo().get(1);
	 * 
	 * System.out.println("Succeed"); }
	 */

	public Ruta GeneraRuta(int H, ArrayList<Pedido> pediParam,
			boolean considerarPlazos, Mapa mapita,
			TablaRuteoAux tabAuxiliarParam) {
		Ruta S;
		this.mapa = mapita;
		this.tabAux = tabAuxiliarParam;
		this.pedidos = pediParam;
		this.cx = mapita.getMaxX();
		this.cy = mapita.getMaxY();

		Ruta SF = new Ruta();
		SF.setCostoRuta(0);

		for (int j = 1; j <= H; j++) {

			reiniciarEstados(pedidos);
			S = Grasp2.GraspPrincipal(pedidos, this.mapa);
			S.setCostoRuta(Calculos.calcularCostoRuta(S.getPedidos()));
			System.out.println("Costo ruta parcial grasp :): "
					+ S.getCostoRuta());
			tabAux = ActualizarTodasFeromonasIniciales(S, tabAux, true, SF);
			// tabAux.dibujar(pedidos);
			SF = ActualizarMejorSolucion(S, SF, considerarPlazos);

			S = generarOrdenEnRecorrido(considerarPlazos);
			S.setCostoRuta(Calculos.calcularCostoRuta(S.getPedidos()));
			System.out.println("Costo ruta parcial " + j + " :"
					+ S.getCostoRuta());
			tabAux = ActualizarTodasFeromonasIniciales(S, tabAux, true, SF);
			// tabAux.dibujar(pedidos);
			SF = ActualizarMejorSolucion(S, SF, considerarPlazos);

			/*
			 * Supongo que la cantidad de feromonas que se suman depende de que
			 * tan eficiente resulto la ruta generada?
			 */

			// MOD BONI: OPTIMIZACION -> FALTA CONSIDERAR QUE NO SE DEBE
			// HACER LA ACTUALIZACION HASTA QUE TODAS LAS HORMIGAS TERMINEN
			// SU PRIMERA PASADA

			// tabAux = ActualizarTodasFeromonasIniciales(S, tabAux);

			/*
			 * if(Constantes.HormigasAlphaFero<0.7) {
			 * Constantes.HormigasAlphaFero = Constantes.HormigasAlphaFero +
			 * 0.05; Constantes.HormigasBetaDist = Constantes.HormigasBetaDist -
			 * 0.05; }
			 */

			// tabAux.ActualizarCambiosFeromonas();
			// tabAux.EvaporarFeromonas(); // Se evaporan en solo una ligera
			// intensidad
		}

		System.out.println("Costo ruta final" + SF.getCostoRuta());
		return SF;
	}

	public void reiniciarEstados(ArrayList<Pedido> pedidos) {
		for (int i = 0; i < pedidos.size(); i++) {
			pedidos.get(i).setVis(false);
		}
	}

	/*
	 * private TablaRuteoAux ActualizarTodasFeromonas(Ruta s, TablaRuteoAux
	 * tabAux2) { // TODO Auto-generated method stub ArrayList<Pedido>
	 * pedidosAuxiliares = s.getPedidos(); int pos1, pos2, inter;
	 * 
	 * if (pedidosAuxiliares.size() > 0) { // La primera vez se empieza desde el
	 * almacen pos1 = 0;
	 * 
	 * pos2 = this.pedidos.indexOf(pedidosAuxiliares.get(0));
	 * 
	 * tabAux2.actualizarFeromonaSinActualizar(pos1, pos2 + 1, s);
	 * 
	 * // Las siguientes ya van uniendo los pedidos for (int i = 0; i <
	 * pedidosAuxiliares.size() - 1; i++) { pos1 =
	 * this.pedidos.indexOf(pedidosAuxiliares.get(i)); pos2 =
	 * this.pedidos.indexOf(pedidosAuxiliares.get(i + 1));
	 * 
	 * if (pos1 > pos2) { inter = pos1; pos1 = pos2; pos2 = inter; }
	 * 
	 * tabAux2.actualizarFeromonaSinActualizar(pos1 + 1, pos2 + 1, s); } }
	 * return tabAux2; }
	 */

	private TablaRuteoAux ActualizarTodasFeromonasIniciales(Ruta s,
			TablaRuteoAux tabAux2, boolean act, Ruta SF) {
		ArrayList<Pedido> pedidosAuxiliares = s.getPedidos();
		int pos1, pos2, inter;

		if (pedidosAuxiliares.size() > 0) {
			// La primera vez se empieza desde el almacen
			pos1 = 0;
			pos2 = this.pedidos.indexOf(pedidosAuxiliares.get(0));

			tabAux2.actualizarFeromonaSinConActualizar(pos1, pos2 + 1, s, act,
					SF);

			// Las siguientes ya van uniendo los pedidos
			for (int i = 0; i < pedidosAuxiliares.size() - 1; i++) {
				pos1 = this.pedidos.indexOf(pedidosAuxiliares.get(i));
				pos2 = this.pedidos.indexOf(pedidosAuxiliares.get(i + 1));

				tabAux2.actualizarFeromonaSinConActualizar(pos1 + 1, pos2 + 1,
						s, act, SF);
			}
			// el ultimo
			pos1 = this.pedidos.indexOf(pedidosAuxiliares.get(pedidosAuxiliares
					.size() - 1));
			pos2 = 0; // almacen
			tabAux2.actualizarFeromonaSinConActualizar(pos1 + 1, pos2, s, act,
					SF);
		}
		return tabAux2;
	}

	private Ruta ActualizarMejorSolucion(Ruta s, Ruta sF,
			boolean considerarPlazos) {

		/*
		 * if (!considerarPlazos) {
		 */if (!sF.getPedidos().isEmpty()) {
			// s.setCostoRuta(Calculos.calcularCostoRuta(s.getPedidos()));

			if (s.getCostoRuta() < sF.getCostoRuta()) {
				sF = s;
			}
		} else {
			// s.setCostoRuta(Calculos.calcularCostoRuta(s.getPedidos()));
			sF = s;
		}

		// }
		// else{}
		return sF;

	}

	public Ruta generarOrdenEnRecorrido(boolean considerarPlazos) {
		int next;
		Ruta R = new Ruta();
		double denom;
		ArrayList<Pedido> S = new ArrayList<Pedido>();

		casasPorVisitar = new ArrayList<Pedido>();
		indice_pedidos_lugar_actual = -1; // empieza en el almacen

		for (int t = 0; t < pedidos.size(); t++) {
			casasPorVisitar.add(pedidos.get(t));
		}

		while (casasPorVisitar.size() > 0) {
			/*
			 * Se usa la tabla de feromonas. Las feromonas se marcan entre
			 * casas, es decir entre una casa i y una casa j. Tambien en la
			 * funcion se usa el factor "distancia" entre una casa i y la j.
			 * Combinando ambos criterios es que se hace el calculo. La funcion
			 * olerSiguienteCasa es una funcion probabilistica. Dentro de esta
			 * funcion se emplea la lista tabu de casas ya visitadas por la
			 * hormiga para descartarla como siguiente posibilidad de visita.
			 */

			next = olerSiguienteCasa(considerarPlazos);
			indice_pedidos_lugar_actual = next;
			// System.out.println("Actual: "+indice_pedidos_lugar_actual+" Next: "+next);
			if (next >= 0) {
				S.add(pedidos.get(next));
			}

		}
		indice_pedidos_lugar_actual = -1; // reinicio la variable para la
											// siguiente hormiga y q empiece en
											// el almacen
		R.setPedidos(S);
		R.setCostoRuta(Calculos.calcularCostoRuta(S));
		return R;
	}

	public int olerSiguienteCasa(boolean considerarPlazos) {

		Hashtable<Integer, Hashtable<Integer, CeldaAux>> tbRuteo = tabAux
				.getTbRuteo();
		int next = 0;

		Integer clave = -20;
		Hashtable<Integer, CeldaAux> tbSubRuteo;

		Enumeration<Integer> e2;
		Integer clave2 = -5, h, clave1mejorProba = -20, clave2mejorProba = -20, clave2valido = 0;
		CeldaAux celda;

		double mejorProba = -1, denom = -10;
		double num, proba = 0.0;

		if (indice_pedidos_lugar_actual == -1) {
			clave = 0;
		} else {
			clave = indice_pedidos_lugar_actual + 1;
		}

		// no hay clave2 xq debe recorrer todos siempre y cuando no hayan
		// sido visitados

		tbSubRuteo = tbRuteo.get(clave);
		e2 = tbSubRuteo.keys();

		while (e2.hasMoreElements()) {
			clave2 = e2.nextElement();
			if (clave2 <= pedidos.size()) {
				h = clave2 - 1;
				if (clave2 != 0) {
					if (clave != clave2) {
						// denom =
						// calcularDenominador(indice_pedidos_lugar_actual+1,
						// casasPorVisitar);
						denom = 1;

						if (casasPorVisitar.contains(pedidos.get(h))) {
							celda = tbSubRuteo.get(clave2);
							num = Math.pow(celda.getFeromonas(),
									Constantes.HormigasAlphaFero)
									* celda.getVisiAlaBbeta();

							if (Double.isNaN(denom)) {
								// System.out.println("salio nan "+clave+" "+clave2);
								proba = num;
							} else {
								proba = num / denom;
								if (Double.isNaN(proba)) {
									proba = num;
								}
							}

							celda.setProba(proba);

							// System.out.println("en medio "+clave + " "
							// +clave2+" " +proba);
							if (mejorProba <= proba) {
								mejorProba = proba;
								clave2mejorProba = clave2;
							}
						}
					}
				}
			}
		}
		clave1mejorProba = clave;

		next = clave2mejorProba - 1; // indice en la lista de pedidos

		if (next >= 0) {
			casasPorVisitar.remove(pedidos.get(next));
		} else {

		}

		/*
		 * }else { // clave de la casa actual pero en la tabla aux claveInter =
		 * indice_pedidos_lugar_actual + 1; // depende de contra // quien lo
		 * analice, // si sera clave1 o // clave2 tbSubRuteo =
		 * tbRuteo.get(claveInter); e2 = tbSubRuteo.keys();
		 * 
		 * while (e2.hasMoreElements()) { clave = e2.nextElement(); h = clave -
		 * 1; if (clave != 0 && clave != claveInter) { // porque el almacen no
		 * // es una opcion // como // proxima casa hasta q se termine todo if
		 * (casasPorVisitar.contains(pedidos.get(h))) { celda =
		 * tbSubRuteo.get(clave);
		 * 
		 * num = Math.pow(celda.getFeromonas(), Constantes.HormigasAlphaFero)
		 * celda.getVisiAlaBbeta();
		 * 
		 * if (denom != 0) { proba = num / denom; } else { proba = num; }
		 * 
		 * celda.setProba(proba); if (mejorProba < proba) { mejorProba = proba;
		 * clave1mejorProba = claveInter; clave2mejorProba = clave; oficial =
		 * clave; }
		 * 
		 * } }
		 * 
		 * } try { next = oficial - 1;
		 * casasPorVisitar.remove(pedidos.get(next)); } catch (Exception e3) {
		 * System.out.println("Error: "); } }
		 */

		return next;
	}

	public double calcularDenominador(int clavePedAct,
			ArrayList<Pedido> casasPorVisitar) {
		Integer claveA;
		Hashtable<Integer, CeldaAux> tbSubRuteo;
		double sumatoria = 0;

		CeldaAux celda;

		Hashtable<Integer, Hashtable<Integer, CeldaAux>> tbRuteo = tabAux
				.getTbRuteo();

		tbSubRuteo = tbRuteo.get(clavePedAct);

		for (Pedido pedidoNoVisitado : casasPorVisitar) {
			claveA = pedidos.indexOf(pedidoNoVisitado) + 1;
			if (claveA != clavePedAct) {
				/*
				 * if (claveA < clavePedAct) { tbSubRuteo = tbRuteo.get(claveA);
				 * celda = tbSubRuteo.get(clavePedAct); sumatoria = sumatoria +
				 * Math.pow(celda.getFeromonas(), Constantes.HormigasAlphaFero)
				 * celda.getVisiAlaBbeta(); } else if (claveA > clavePedAct) {
				 */
				try {
					celda = tbSubRuteo.get(claveA);
					sumatoria = sumatoria
							+ Math.pow(celda.getFeromonas(),
									Constantes.HormigasAlphaFero)
							* celda.getVisiAlaBbeta();
				} catch (Exception t) {

					// System.out.println("clave "+clavePedAct+
					// "clave2 "+claveA);
					// System.out.println("fero "+celda.getFeromonas());
				}
			}
		}

		return sumatoria;

	}

	/*
	 * public ArrayList<Ruta> asignarVehiculo(Ruta r) { ArrayList<Ruta> lRutas =
	 * new ArrayList<Ruta>(); int cantPaquetes = cantPaquetes(r); if
	 * (cantPaquetes <= 4) { // Busco asignar en una moto int i; if ((i =
	 * motoDisponible()) >= 0) { // Hay motoDisponible
	 * r.getVehiculo().setIdVehiculo(i); lRutas.add(r); return lRutas; // Le
	 * AGREGO LA MOTO
	 * 
	 * } else if ((i = autoDisponible()) >= 0) { // Hay auto
	 * r.getVehiculo().setIdVehiculo(i); lRutas.add(r); return lRutas; // Le
	 * AGREGO AUTO
	 * 
	 * } else {// COLAPSO NO HAY VEHICULOS DISPONIBLES // No hay return null; }
	 * } else if (cantPaquetes >= 17) {// ES NUESTRO NUMERO ACEPTABLE // Busco
	 * asginar en un carro int i; if ((i = autoDisponible()) >= 0) { // Hay auto
	 * r.getVehiculo().setIdVehiculo(i); lRutas.add(r); return lRutas;
	 * 
	 * // Le AGREGO AUTO
	 * 
	 * } else if (motoDisponible() >= 0) { // dividir en zonas ArrayList<Ruta>
	 * rutas = new ArrayList<Ruta>(); dividirRuta(r, rutas); // Ver motos
	 * disponibles int k = 0; int j = 0; for (j = 0; j < rutas.size(); j++) if
	 * ((k = motoDisponible()) >= 0)
	 * rutas.get(j).getVehiculo().setIdVehiculo(k);// Asigno // moto else return
	 * null; return rutas;// QUIERE DECIR QUE ASIGNO MOTOS A TODAS LAS //
	 * SUBRUTAS // if (j==rutas.size() && k!=-1) return 1;//QUIERE DECIR QUE //
	 * ASIGNO MOTOS A TODAS LAS SUBRUTAS
	 * 
	 * } // else break;//COLAPSO NO HAY VEHICULOS DISPONIBLES } else {// Hay
	 * entre 4 y 16 paquetes // PRIMERO BUSCA METERLO EN MOTOS int i = 0; if
	 * (motoDisponible() >= 0) {
	 * 
	 * // dividir en zonas ArrayList<Ruta> rutas = new ArrayList<Ruta>();
	 * dividirRuta(r, rutas); // Ver motos disponibles int k = 0; int j = 0; for
	 * (j = 0; j < rutas.size(); j++) if ((k = motoDisponible()) >= 0)
	 * rutas.get(j).getVehiculo().setIdVehiculo(k);// Asigno // moto else return
	 * null; return rutas; }// ASUMIRA Y LO METERA EN UN CARRO else if ((i =
	 * autoDisponible()) >= 0) { r.getVehiculo().setIdVehiculo(i);
	 * lRutas.add(r); return lRutas; } else return null; }
	 * 
	 * return null; // ACA DECIA RETURN 0 }
	 */
	/*
	 * public ArrayList<Ruta> dividirRuta(Ruta r, ArrayList<Ruta> rutas) {
	 * 
	 * int cantPaquetes = cantPaquetes(r); // Condicion de parada if
	 * (cantPaquetes <= 4) { rutas.add(r); return rutas; } // Divide
	 * 
	 * int cx, cy; int costoHorizontal, costoVertical; Ruta S1, S2;
	 * 
	 * ArrayList<Integer> centro = new ArrayList<Integer>(2); centro =
	 * calcularCentroX(r); cx = centro.get(0); cy = centro.get(1); // Suma de
	 * los costos de la ruta de abajo y de arriba incluyendo ida y // retorno al
	 * almacen ArrayList<Ruta> lRHorizontal = new ArrayList<Ruta>();
	 * lRHorizontal = partirRuta(r, cx, cy, 'H'); costoHorizontal = (int)
	 * (lRHorizontal.get(0).getCostoRuta() + lRHorizontal
	 * .get(1).getCostoRuta());
	 * 
	 * Falta calcular El costo de las Rutas en Horizontal
	 * 
	 * ArrayList<Ruta> lRVertical = new ArrayList<Ruta>(); lRVertical =
	 * partirRuta(r, cx, cy, 'V'); costoVertical = (int)
	 * (lRHorizontal.get(0).getCostoRuta() + lRHorizontal
	 * .get(1).getCostoRuta());
	 * 
	 * if (costoHorizontal >= costoVertical) { S1 = lRHorizontal.get(0); S2 =
	 * lRHorizontal.get(1); } else { S1 = lRVertical.get(0); S2 =
	 * lRVertical.get(1); }
	 * 
	 * dividirRuta(S1, rutas); dividirRuta(S2, rutas);
	 * 
	 * return rutas; }
	 */
	


	

	/*
	 * // Vehiculo v, ArrayList<Pedido> public boolean
	 * UnSoloVehiculoNoAbastece(Ruta r) { // By Henry // Falta ver los tipos de
	 * datos int capacidad = 0; /* if (v.getTipoVehiculo()==Constantes.tipoAuto)
	 * 
	 * capacidad = TipoVehiculo.getCapacidad(Constantes.tipoAuto); else
	 * capacidad = TipoVehiculo.getCapacidad(Constantes.tipoMoto); boolean flag
	 * = false; int capacidadRuta = 0; for (int i = 0; i < S.size(); i++)
	 * capacidadRuta += (S.get(i).getCantPaquetes() * tamPaquete);
	 * 
	 * if (capacidad < capacidadRuta) return false; else return true;
	 */
	// boolean flag = false;

	// Ahora con la cant de paquetes tengo que ver la disponibilidad de
	// vehiculo

	// }

	/*
	 * public ArrayList<Vehiculo> getListaVehiculos() { return listaVehiculos; }
	 * 
	 * public void setListaVehiculos(ArrayList<Vehiculo> listaVehiculos) {
	 * this.listaVehiculos = listaVehiculos; }
	 */
	/*
	 * public ArrayList<Integer> calcularCentroX(Ruta r) { ArrayList<Integer>
	 * centro = new ArrayList<Integer>(2); int cX = 0; int cY = 0;
	 * ArrayList<Pedido> pedidos = r.getPedidos(); for (int i = 0; i <
	 * pedidos.size(); i++) { cX += pedidos.get(i).getPosX(); cY +=
	 * pedidos.get(i).getPosY(); } cX = cX / pedidos.size(); cY = cY /
	 * pedidos.size(); centro.add(0, cX); centro.add(1, cY); return centro; }
	 */

	public ArrayList<Ruta> partirRuta(Ruta r, int cX, int cY, String criterio) {

		// Saco los pedidos de la ruta 'R' que viene de Parametro
		ArrayList<Pedido> pedidosApartir = new ArrayList<Pedido>();
		pedidosApartir = r.getPedidos();

		// Creo los un ArrayLista de Pedidos que vamos a agregar a cada Ruta
		ArrayList<Pedido> pedido0 = new ArrayList<Pedido>();
		ArrayList<Pedido> pedido1 = new ArrayList<Pedido>();

		/*
		 * Para la particion Horizontal el pedido 0 es el Superior y el pedido 1
		 * es el Inferior
		 * 
		 * Para la particion Vertical el pedido 0 es la Izquierda y el pedido 1
		 * es la Derecha
		 */

		// Agrego los pedidos a los pedidos H Superior e Inferior
		for (int i = 0; i < pedidosApartir.size(); i++)
			if (criterio.equals("H")) {// Si es Horizontal
				if (pedidosApartir.get(i).getPosY() <= cY)
					pedido0.add(pedidosApartir.get(i));
				else
					pedido1.add(pedidosApartir.get(i));
			}// Si es Vertical
			else {
				if (pedidosApartir.get(i).getPosX() <= cX)
					pedido0.add(pedidosApartir.get(i));
				else
					pedido1.add(pedidosApartir.get(i));
			}

		/*
		 * Para la ruta Horizontal el pedido 0 es el Superior y el pedido 1 es
		 * el Inferior
		 * 
		 * Para la ruta Vertical el pedido 0 es la Izquierda y el pedido 1 es la
		 * Derecha
		 */

		Ruta ruta0 = new Ruta();
		Ruta ruta1 = new Ruta();
		ruta0 = Grasp2.GraspPrincipal(pedido0, this.mapa);
		ruta1 = Grasp2.GraspPrincipal(pedido1, this.mapa);

		// Defino un ArrayList donde van a estar las dos rutas
		ArrayList<Ruta> rutas = new ArrayList<Ruta>();

		// Agrego las Rutas divididas al ArrayList definido al inicio
		rutas.add(ruta0);
		rutas.add(ruta1);

		return rutas;
	}

	public Mapa getMapa() {
		return mapa;
	}

	public void setMapa(Mapa mapa) {
		this.mapa = mapa;
	}

	

	

	// public void costoRuta(ArrayList<Ruta> rutas) {
	//
	// }


}